﻿Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Globalization
Imports System.Reflection
Imports System.Workflow.ComponentModel

Namespace System.Workflow.ComponentModel.Design
    Friend Class ParameterInfoBasedPropertyDescriptor
        Inherits PropertyDescriptor
        ' Methods
        Friend Sub New(ByVal componentType As Type, ByVal paramInfo As ParameterInfo, ByVal avoidDuplication As Boolean, ByVal ParamArray attributes As Attribute())
            MyBase.New(If((paramInfo.Position = -1), "(ReturnValue)", paramInfo.Name), attributes)
            Me.desc = String.Empty
            If (componentType Is Nothing) Then
                Throw New ArgumentNullException("componentType")
            End If
            If (paramInfo Is Nothing) Then
                Throw New ArgumentNullException("paramInfo")
            End If
            If (paramInfo.ParameterType Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ParameterTypeResolution", New Object() { paramInfo.Name }))
            End If
            Me.componentType = componentType
            Me.parameter = paramInfo
            Me.avoidDuplication = avoidDuplication
            Me.parameterType = paramInfo.ParameterType
            If ((Not paramInfo.ParameterType Is Nothing) AndAlso (paramInfo.ParameterType.IsByRef OrElse (paramInfo.IsIn AndAlso paramInfo.IsOut))) Then
                SR.GetString("Ref")
            ElseIf (paramInfo.IsOut OrElse (paramInfo.Name Is Nothing)) Then
                SR.GetString("Out")
            Else
                SR.GetString("In")
            End If
            Me.desc = SR.GetString("ParameterDescription", New Object() { paramInfo.ParameterType.FullName })
        End Sub

        Friend Sub New(ByVal componentType As Type, ByVal propertyName As String, ByVal propertyType As Type, ByVal avoidDuplication As Boolean, ByVal ParamArray attributes As Attribute())
            MyBase.New(propertyName, attributes)
            Me.desc = String.Empty
            If (componentType Is Nothing) Then
                Throw New ArgumentNullException("componentType")
            End If
            If (propertyType Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ParameterTypeResolution", New Object() { propertyName }))
            End If
            Me.componentType = componentType
            Me.parameterType = propertyType
            Me.avoidDuplication = avoidDuplication
            Me.desc = SR.GetString("InvokeParameterDescription", New Object() { propertyType.FullName.ToString })
        End Sub

        Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
            Return False
        End Function

        Friend Shared Function FindMatchingMember(ByVal name As String, ByVal ownerType As Type, ByVal ignoreCase As Boolean) As MemberInfo
            Dim info2 As MemberInfo
            For Each info2 In ownerType.GetMembers((BindingFlags.FlattenHierarchy Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance)))))
                If info2.Name.Equals(name, If(ignoreCase, StringComparison.CurrentCultureIgnoreCase, StringComparison.CurrentCulture)) Then
                    Return info2
                End If
            Next
            Return Nothing
        End Function

        Public Overrides Function GetEditor(ByVal editorBaseType As Type) As Object
            Return TypeDescriptor.GetEditor(Me.PropertyType, editorBaseType)
        End Function

        Private Function GetParameterBindings(ByVal component As Object) As WorkflowParameterBindingCollection
            Dim bindings As WorkflowParameterBindingCollection = Nothing
            If (Not component.GetType.GetProperty("ParameterBindings", (BindingFlags.ExactBinding Or (BindingFlags.GetProperty Or (BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or BindingFlags.Instance)))), Nothing, GetType(WorkflowParameterBindingCollection), New Type(0  - 1) {}, New ParameterModifier(0  - 1) {}) Is Nothing) Then
                bindings = [TryCast](component.GetType.InvokeMember("ParameterBindings", (BindingFlags.ExactBinding Or (BindingFlags.GetProperty Or (BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or BindingFlags.Instance)))), Nothing, component, New Object(0  - 1) {}, CultureInfo.InvariantCulture),WorkflowParameterBindingCollection)
            End If
            Return bindings
        End Function

        Public Shared Function GetParameterPropertyName(ByVal componentType As Type, ByVal paramName As String) As String
            Dim str As String = paramName
            If (Not ParameterInfoBasedPropertyDescriptor.FindMatchingMember(paramName, componentType, False) Is Nothing) Then
                str = ("(Parameter) " & paramName)
            End If
            Return str
        End Function

        Public Overrides Function GetValue(ByVal component As Object) As Object
            Dim parameterBindings As WorkflowParameterBindingCollection = Me.GetParameterBindings(component)
            Dim name As String = Me.Name
            Dim key As String = If(name.StartsWith("(Parameter) ", StringComparison.Ordinal), name.Substring("(Parameter) ".Length), name)
            If ((parameterBindings Is Nothing) OrElse Not parameterBindings.Contains(key)) Then
                Return Nothing
            End If
            If parameterBindings.Item(key).IsBindingSet(WorkflowParameterBinding.ValueProperty) Then
                Return parameterBindings.Item(key).GetBinding(WorkflowParameterBinding.ValueProperty)
            End If
            Return parameterBindings.Item(key).GetValue(WorkflowParameterBinding.ValueProperty)
        End Function

        Public Overrides Sub ResetValue(ByVal component As Object)
            If ((Not Me.PropertyType Is Nothing) AndAlso Not Me.PropertyType.IsValueType) Then
                Me.SetValue(component, Nothing)
            End If
        End Sub

        Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
            If (Not component Is Nothing) Then
                Dim site As IServiceProvider = MemberDescriptor.GetSite(component)
                Dim dispatcher As ComponentChangeDispatcher = If((Not site Is Nothing), New ComponentChangeDispatcher(site, component, Me), Nothing)
                Try 
                    Dim parameterBindings As WorkflowParameterBindingCollection = Me.GetParameterBindings(component)
                    If (Not parameterBindings Is Nothing) Then
                        Dim key As String = String.Empty
                        If Me.Name.StartsWith("(Parameter) ", StringComparison.Ordinal) Then
                            key = Me.Name.Substring("(Parameter) ".Length)
                        Else
                            key = Me.Name
                        End If
                        Dim item As WorkflowParameterBinding = Nothing
                        If parameterBindings.Contains(key) Then
                            item = parameterBindings.Item(key)
                        Else
                            item = New WorkflowParameterBinding(key)
                            parameterBindings.Add(item)
                        End If
                        If TypeOf value Is ActivityBind Then
                            item.SetBinding(WorkflowParameterBinding.ValueProperty, [TryCast](value,ActivityBind))
                        Else
                            item.SetValue(WorkflowParameterBinding.ValueProperty, value)
                        End If
                        Me.OnValueChanged(component, EventArgs.Empty)
                    End If
                Catch exception As Exception
                    If (TypeOf exception Is TargetInvocationException AndAlso (Not exception.InnerException Is Nothing)) Then
                        Throw exception.InnerException
                    End If
                    Throw exception
                Finally
                    If (Not dispatcher Is Nothing) Then
                        dispatcher.Dispose
                    End If
                End Try
            End If
        End Sub

        Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
            Return False
        End Function


        ' Properties
        Public Overrides ReadOnly Property Attributes As AttributeCollection
            Get
                Dim list As New ArrayList
                list.AddRange(MyBase.Attributes)
                list.AddRange(TypeDescriptor.GetAttributes(Me.PropertyType))
                Return New AttributeCollection(DirectCast(list.ToArray(GetType(Attribute)), Attribute()))
            End Get
        End Property

        Public Overrides ReadOnly Property Category As String
            Get
                Return SR.GetString("Parameters")
            End Get
        End Property

        Public Overrides ReadOnly Property ComponentType As Type
            Get
                Return Me.componentType
            End Get
        End Property

        Public Overrides ReadOnly Property Converter As TypeConverter
            Get
                Return New ActivityBindTypeConverter
            End Get
        End Property

        Public Overrides ReadOnly Property Description As String
            Get
                Return Me.desc
            End Get
        End Property

        Public Overrides ReadOnly Property DisplayName As String
            Get
                Return Me.Name
            End Get
        End Property

        Public Overrides ReadOnly Property IsReadOnly As Boolean
            Get
                Return False
            End Get
        End Property

        Public Overrides ReadOnly Property Name As String
            Get
                If Me.avoidDuplication Then
                    Return ParameterInfoBasedPropertyDescriptor.GetParameterPropertyName(Me.componentType, MyBase.Name)
                End If
                Return MyBase.Name
            End Get
        End Property

        Friend ReadOnly Property ParameterType As Type
            Get
                Dim parameterType As Type = Me.parameterType
                If parameterType.IsByRef Then
                    parameterType = parameterType.GetElementType
                End If
                Return parameterType
            End Get
        End Property

        Public Overrides ReadOnly Property PropertyType As Type
            Get
                Dim parameterType As Type = Me.ParameterType
                If (parameterType Is Nothing) Then
                    parameterType = GetType(ActivityBind)
                End If
                Return parameterType
            End Get
        End Property


        ' Fields
        Private avoidDuplication As Boolean
        Private componentType As Type
        Private desc As String
        Private parameter As Object
        Private Const parameterPrefix As String = "(Parameter) "
        Private parameterType As Type
    End Class
End Namespace

